/*
 * Copyright 2020-2025 the original author or authors.
 * You cannot use this file unless authorized by the author.
 */

package org.ipig.data.eh.impl;

import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang.StringUtils;
import org.ehcache.CacheManager;
import org.ehcache.PersistentCacheManager;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ExpiryPolicyBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.expiry.ExpiryPolicy;
import org.ipig.commons.conf.ehcache.EhcacheConf;
import org.ipig.commons.context.JavaContext;
import org.ipig.constants.CacheCnst;
import org.ipig.data.eh.EhcacheService;

import java.io.File;
import java.io.Serializable;
import java.time.Duration;

/**
 * EhcacheManager
 *
 * @author <a href="mailto:comchnts@163.com">chinats</a>
 * @version $$Id: EhcacheManager.java 14 2020-04-17 06:56:19Z ts <comchnts@163.com> $$
 * @since 1.0
 */
public class EhcacheManager implements EhcacheService<CacheManager>, Serializable {
    @Getter
    @Setter
    private EhcacheConf conf;
    //1. 创建缓存管理器
    private volatile transient CacheManager cacheManager = null;

    public EhcacheManager() {
    }

    public EhcacheManager(EhcacheConf conf) {
        this.conf = conf;
    }


    @Override
    public CacheManager getClient() {
        if (cacheManager == null) {
            synchronized (this) {
                if (null == cacheManager) {
                    if (conf == null) {
                        new IllegalArgumentException("未设置EhcacheConf对象");
                    }
                    Duration duration =null;
                    //初始化Ehcache配置
                    if (conf.getCacheTimeSeconds() == 0) {
                        duration = Duration.ofSeconds(Integer.MAX_VALUE);
                    }else{
                        duration = Duration.ofSeconds(conf.getCacheTimeSeconds());
                    }
                    ExpiryPolicy<Object, Object> expiryPolicy = ExpiryPolicyBuilder.timeToIdleExpiration(duration);
                    if (StringUtils.equalsIgnoreCase(conf.getExpiryPolicy(), CacheCnst.ExpiryPolicy.TTL.code)) {
                        expiryPolicy = ExpiryPolicyBuilder.timeToLiveExpiration(duration);
                    } else {
                        expiryPolicy = ExpiryPolicyBuilder.timeToIdleExpiration(duration);
                    }

                    ResourcePoolsBuilder rpb = ResourcePoolsBuilder.newResourcePoolsBuilder();
                    if (conf.isDiskPersistable()) {//表示将对象写到磁盘中
                        rpb = rpb.disk(conf.getDiskSize(), MemoryUnit.MB, conf.isRsrcPoolPersistable());
                    }
                    if (conf.getHeapSize() > 0) {//heap堆内内存,不需要序列化，速度最快
                        rpb = rpb.heap(conf.getHeapSize(), MemoryUnit.MB);
                    }
                    if (conf.getOffHeapSize() > 0) {//offheap堆外内存，将你的对象从堆中脱离出来序列化，然后存储在一大块内存中仍然在RAM中。对象在这种状态下不能直接使用，它们必须首先反序列化，也不受垃圾收集。序列化和反序列化将会影响部分性能
                        rpb = rpb.offheap(conf.getOffHeapSize(), MemoryUnit.MB);
                    }
                    CacheConfiguration<String, String> cacheConfBuilder = CacheConfigurationBuilder
                            .newCacheConfigurationBuilder(String.class, String.class, rpb)
                            .withSizeOfMaxObjectGraph(conf.getMaxObjGraph()) //统计对象大小时，对象图遍历深度
                            .withSizeOfMaxObjectSize(conf.getMaxObjSize(), MemoryUnit.KB) //来设置最大的对象大小,设置单个Entry的最大size,超过大小的将无法存到cache
                            .withExpiry(expiryPolicy) //过期策略
                            .build();

                    String rootDirectory = JavaContext.getIoTmpDir();
                    if (StringUtils.isNotBlank(conf.getRootDirectory())) {
                        rootDirectory = conf.getRootDirectory();
                        if (!(new File(rootDirectory)).exists()) {
                            rootDirectory = JavaContext.getIoTmpDir();
                        }
                    }

                    CacheManagerBuilder<CacheManager> cmb = CacheManagerBuilder.newCacheManagerBuilder();
                    cacheManager = cmb.with(CacheManagerBuilder.persistence(rootDirectory + File.separator + conf.getCacheAlias()))
                            .withDefaultSizeOfMaxObjectSize(conf.getDfltSizeOfMaxObjSize(), MemoryUnit.KB) //设置单个Entry的大小限制
                            .withDefaultSizeOfMaxObjectGraph(conf.getDfltSizeOfMaxObjGraph())
                            .withCache(conf.getCacheAlias(), cacheConfBuilder)
                            .build(true);
                }
            }
        }
        return cacheManager;
    }
}